#ifndef _MEMORYPOOL_HPP_
#define _MEMORYPOOL_HPP_

#include <iostream>
#include <mutex>
#include "log.hpp"

////一个内存块的最大内存大小,可以扩展
#define MAX_MEMORY_SIZE 256

class MemoryPool;

// 内存块
struct MemoryBlock
{
    MemoryBlock *pNext; // 下一块内存块
    bool bUsed;         // 是否使用
    bool bBelong;       // 是否属于内存池
    MemoryPool *pMem;   // 属于哪个池子(内存池可以有很多个)
};

class MemoryPool
{
public:
    MemoryPool(size_t nSize = 128, size_t nBlock = 10)
    {
        // 相当于申请10块内存，每块内存是1024
        _nSize = nSize;
        _nBlock = nBlock;
        _pHeader = NULL;
        _pBuffer = NULL;
    }
    virtual ~MemoryPool()
    {
        if (_pBuffer != NULL)
        {
            free(_pBuffer);
        }
    }
    // 申请内存
    void *AllocMemory(size_t nSize)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        // 如果首地址为空，说明没有申请空间
        if (_pBuffer == NULL)
        {
            InitMemory();
        }
        MemoryBlock *pRes = NULL;
        // 如果内存池不够用时，需要重新申请内存
        if (_pHeader == NULL) // 不可能出现
        {
            pRes = (MemoryBlock *)malloc(nSize + sizeof(MemoryBlock));
            pRes->bBelong = false;
            pRes->bUsed = false;
            pRes->pNext = NULL;
            pRes->pMem = NULL;
        }
        else
        {
            pRes = _pHeader;
            _pHeader = _pHeader->pNext;
            pRes->bUsed = true;
        }
        logmessage(DEBUG, "MemoryPool::AllocMemory success");
        // 返回只返回头后面的信息
        return ((char *)pRes + sizeof(MemoryBlock));
    }

    // 释放内存
    void FreeMemory(void *p)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        // 和申请内存刚好相反，这里需要包含头，然后全部释放
        MemoryBlock *pBlock = ((MemoryBlock *)p - sizeof(MemoryBlock));
        if (pBlock->bBelong)
        {
            pBlock->bUsed = false;
            // 循环链起来
            pBlock->pNext = _pHeader;
            pBlock = _pHeader;
        }
        else
        {
            // 不属于内存池直接释放就可以
            free(pBlock);
        }
    }
    // 初始化内存块
    void InitMemory()
    {
        if (_pBuffer)
            return;
        // 计算每块的大小
        size_t PoolSize = _nSize + sizeof(MemoryBlock); // 块大小 : 指定存储数据段 + 块信息段
        // 计算需要申请多少内存
        size_t BuffSize = PoolSize * _nBlock; // 一次性全部申请(对内存做管理)
        _pBuffer = (char *)malloc(BuffSize);  // struct MemoryBlock段 + 数据段
        if(_pBuffer == NULL)
        {
            perror("malloc failed!");
            return;
        }
        // if (_pBuffer == NULL)
        // {
        //     do
        //     {
        //         _pBuffer = (char *)malloc(BuffSize);
        //     } while (_pBuffer == NULL);
        //     std::cout << "first malloc failed ! while malloc : success! " << std::endl;
        // }
        // 初始化头
        _pHeader = (MemoryBlock *)_pBuffer;
        _pHeader->bUsed = false;
        _pHeader->bBelong = true;
        _pHeader->pMem = this;
        // 初始化_nBlock块，并且用链表的形式连接
        // 保存头指针
        MemoryBlock *tmp1 = _pHeader;
        for (size_t i = 1; i < _nBlock; i++) // 对剩余_nBlock-1块用链表管理
        {
            MemoryBlock *tmp2 = (MemoryBlock *)(_pBuffer + i * PoolSize);
            tmp2->bUsed = false;
            tmp2->pNext = NULL;
            tmp2->bBelong = true;
            _pHeader->pMem = this;
            tmp1->pNext = tmp2;
            tmp1 = tmp2;
        }
    }

protected:
    // 内存首地址（第一块内存的地址）
    char *_pBuffer;
    // 内存块头
    MemoryBlock *_pHeader;
    // 内存块大小
    size_t _nSize;
    // 多少块
    size_t _nBlock;

    std::mutex _mutex;
};

// 可以使用模板传递参数
template <size_t nSize, size_t nBlock>
class MemoryPoolor : public MemoryPool
{
    using MemoryPool::_nSize;
    using MemoryPool::_nBlock;

public:
    MemoryPoolor()
    {
        _nSize = nSize;
        _nBlock = nBlock;
    }
};

// 需要重新对内存池就行管理
class ManagerPool
{
public:
    static ManagerPool* Instance()
    {
        static ManagerPool memPool;
        logmessage(DEBUG, "ManagerPool::Instance success");
        return &memPool;
    }

    void *AllocMemory(size_t nSize)
    {
        if (nSize < MAX_MEMORY_SIZE)
        {
            return _Alloc[nSize]->AllocMemory(nSize);
        }
        else
        {
            MemoryBlock *pRes = (MemoryBlock *)malloc(nSize + sizeof(MemoryBlock));
            pRes->bBelong = false;
            pRes->bUsed = true;
            pRes->pMem = NULL;
            pRes->pNext = NULL;
            return ((char *)pRes + sizeof(MemoryBlock));
        }
        logmessage(DEBUG, "ManagerPool::AllocMemory success");
    }

    // 释放内存
    void FreeMemory(void *p)
    {
        MemoryBlock *pBlock = (MemoryBlock *)((char *)p - sizeof(MemoryBlock));
        // 释放内存池
        if (pBlock->bBelong)
        {
            pBlock->pMem->FreeMemory(p);
        }
        else
        {
            free(pBlock);
        }
    }

private:
    ManagerPool()
    {
        InitArray(0, 128, &_memory128);
        InitArray(129, 256, &_memory256);
        logmessage(DEBUG, "ManagerPool success");
    }

    ManagerPool(const ManagerPool& m) = delete;
    ManagerPool& operator=(const ManagerPool& m) = delete;

    ~ManagerPool() = default;    

    void InitArray(const int &nBegin, const int &nEnd, MemoryPool *pMemPool)
    {
        for (int i = nBegin; i <= nEnd; i++)
        {
            _Alloc[i] = pMemPool;
        }
    }
    // 可以根据不同内存块进行分配
    MemoryPoolor<128, 1000> _memory128;
    MemoryPoolor<256, 1000> _memory256;
    // 映射数组
    MemoryPool *_Alloc[MAX_MEMORY_SIZE + 1];
};
#endif